/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package semant.second_pass;

import semant.Env;
import symbol.ClassInfo;
import symbol.MethodInfo;
import symbol.Symbol;
import symbol.VarInfo;
import syntaxtree.*;

/**
 *
 * @author Thiago
 */
public class StatementHandler extends VisitorAdapter {

    Env env;

    ClassInfo info;

    MethodInfo method;

    StatementHandler(Env e, ClassInfo c, MethodInfo m)
    {
        env = e;
        info = c;
        method = m;
    }

    static void secondPass(Env e,ClassInfo c, MethodInfo m ,Statement s)
    {

        StatementHandler h = new StatementHandler(e,c,m);

        s.accept(h);
    }

    public void visit(While node)
    {
        Type condition = ExpHandler.secondPass(env,info,method,node.condition);

        if(!(condition instanceof BooleanType))
        {
            env.err.Error(node,new Object[]{"Tipo invalido para condicao do \'while\'.",
                                            "Esperado: Boolean",
                                            "Encontrado: "+ condition});
        }

        StatementHandler.secondPass(env,info,method,node.body);
    }

    public void visit(If node)
    {

        Type condition = ExpHandler.secondPass(env, info, method,node.condition);

        if(!(condition instanceof BooleanType))
        {
            env.err.Error(node,new Object[]{"Tipo invalido para condicao do \'if\'.",
                                            "Esperado: Boolean",
                                            "Encontrado: "+ condition});
        }

        StatementHandler.secondPass(env, info, method, node.thenClause);

        if(node.elseClause!=null)
            StatementHandler.secondPass(env, info, method, node.elseClause);
    }

    public void visit(ArrayAssign node)
    {
        VarInfo id =  getVariable(info, method,Symbol.symbol(node.var.s));

        if(id == null)
        {
            env.err.Error(node, new Object[]{
                        "Identificador \'" + node.var.s + "\' nao definido no metodo atual."});

        }
        else if(!(id.type instanceof IntArrayType))
        {
            env.err.Error(node,new Object[]{"Tipo invalido para atribuição.",
                                            "Esperado: ArrayInt",
                                            "Encontrado: "+ id.type});
        }

        Type pos = ExpHandler.secondPass(env,info,method,node.index);

        if(!(pos instanceof IntegerType))
        {
            env.err.Error(node,new Object[]{"Tipo invalido para posicao do vetor'.",
                                            "Esperado: Integer",
                                            "Encontrado: "+ pos});
        }

        Type value = ExpHandler.secondPass(env,info,method,node.value);

        if(!(value instanceof IntegerType))
        {
            env.err.Error(node,new Object[]{"Tipo invalido de valor que sera inserido no vetor'.",
                                            "Esperado: Integer",
                                            "Encontrado: "+ value});
        }

    }

    public void visit(Assign node)
    {
        VarInfo id = StatementHandler.getVariable(info, method,Symbol.symbol(node.var.s));

        if(id == null)
        {
            env.err.Error(node, new Object[]{
                        "Identificador \'" + id.name + "\' nao definido no metodo atual."});
        }

        Type value = ExpHandler.secondPass(env,info,method,node.exp);


        if(id!=null && !Type.compatible(env,id.type,value))
                env.err.Error(node,new Object[]{"Expressao de atribuicao incompativel com o tipo de variavel.",
                                            "Esperado: " + id.type,
                                            "Encontrado: " + value});


    }

    public void visit(Block node)
    {
        StatementListHandler.secondPass(env,info,method,node.body);
    }

    public void visit(Print node)
    {

        Type exp = ExpHandler.secondPass(env,info,method,node.exp);

        if(!(exp instanceof IntegerType))
        {
            env.err.Error(node,new Object[]{"Tipo invalido para imprimir'.",
                                            "Esperado: Integer",
                                            "Encontrado: " + exp});
        }

    }

    private static VarInfo getVariable(ClassInfo c, MethodInfo m, Symbol name)
    {

        VarInfo var = null;

        if (!(m == null)) {
            if (!m.localsTable.containsKey(name)) {
                if (!m.formalsTable.containsKey(name)) {
                    if (!(c == null)) {
                        if (c.attributes.containsKey(name)) {
                            var = c.attributes.get(name);
                        }
                    }
                } else {
                    var = m.formalsTable.get(name);
                }
            } else {
                var = m.localsTable.get(name);
            }
        }

        return var;
    }

}
